import config
import wx
import sys
import time
import zlib
import irc
import strings as s

def colourFromNick(nick):
    crc = zlib.crc32(nick)
    red = crc & 255
    green = (crc / 256) & 127
    blue = (crc / 65535) & 255
    return wx.Colour(red, green, blue)

def invertColour(colour):
    red, green, blue = colour.Get()
    red, green, blue = 255-red, 255-green, 255-blue
    return wx.Colour(red, green, blue)

class ColourTuple(wx.Colour):
    def __init__(self, tuple):
        wx.Colour.__init__(self, tuple[0], tuple[1], tuple[2])

class NicknameDialog(wx.Dialog):
    def __init__(self, parent):
        wx.Dialog.__init__(self, parent, -1, s.window_title_nickname)
        
        panel = wx.Panel(self)
        sbox = wx.BoxSizer()
        panel.SetSizer(sbox)
        sizer = wx.GridBagSizer(vgap = 5, hgap = 5)
        sizer.AddGrowableCol(2)
        sizer.AddGrowableRow(2)
        sbox.Add(sizer, 1, wx.EXPAND | wx.ALL, 5) 
        
        lbl0 = wx.StaticText(panel, -1, s.help_nickname)
        sizer.Add(lbl0, (0,0), (1,3), wx.EXPAND)
        
        lbl1 = wx.StaticText(panel, -1, s.label_nickname)
        sizer.Add(lbl1, (1,0), (1,1), wx.EXPAND)
        self.txt = wx.TextCtrl(panel, -1, "")
        sizer.Add(self.txt, (1,1), (1,2), wx.EXPAND)
        
        btn_cancel = wx.Button(panel, -1, s.button_cancel)
        sizer.Add(btn_cancel, (2,1), (1,1), wx.EXPAND)
        btn_ok = wx.Button(panel, -1, s.button_ok)
        sizer.Add(btn_ok, (2,2), (1,1), wx.EXPAND)
        
        sbox.Fit(self)


class OutputPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.text = wx.TextCtrl(self, style=wx.TE_READONLY |
                                wx.TE_MULTILINE |
                                wx.TE_AUTO_URL |
                                wx.BORDER_NONE |
                                wx.TE_AUTO_SCROLL |
                                wx.TE_RICH2)
        sizer = wx.BoxSizer()
        sizer.Add(self.text, 1, wx.EXPAND)
        self.SetSizerAndFit(sizer)
    
    def writeLine(self, colour, text):
        lt = time.localtime()
        timestamp = "%02d:%02d:%02d" % (lt[3], lt[4], lt[5]) 
        self.writeText(ColourTuple(config.colour_action), "\r\n(%s) " % timestamp)
        self.writeText(colour, text)

        # workaround scroll bug 
        # https://sourceforge.net/tracker/?func=detail&atid=109863&aid=665381&group_id=9863
        self.text.ScrollLines(-1)
        self.text.ShowPosition(self.text.GetLastPosition())
        
    def writeText(self, colour, text):
        self.text.SetDefaultStyle(wx.TextAttr(colour))
        self.text.write(text)
        
        
    def processMessage(self, msg):
        if (msg.command == "chat"):
            self.writeLine(colourFromNick(msg.nick), msg.nick + ": ")
            self.writeText(ColourTuple(config.colour_text), msg.text)
        
        if (msg.command == "join"):
            self.writeLine(ColourTuple(config.colour_action), "--> ")
            self.writeText(ColourTuple(config.colour_action), msg.nick + " ")
            self.writeText(ColourTuple(config.colour_action), "has joined")

        if (msg.command == "part"):
            self.writeLine(ColourTuple(config.colour_action), "<-- ")
            self.writeText(ColourTuple(config.colour_action), msg.nick + " ")
            self.writeText(ColourTuple(config.colour_action), "has left")
        
        if (msg.command == "quit"):
            self.writeLine(ColourTuple(config.colour_action), "<-- ")
            self.writeText(ColourTuple(config.colour_action), msg.nick + " ")
            self.writeText(ColourTuple(config.colour_action), "has gone off-line")
        
        if (msg.command == "im"):
            self.writeLine(ColourTuple(config.colour_im), msg.nick + ": ")
            self.writeText(ColourTuple(config.colour_text), msg.text)
        
        if (msg.command == "echo"):
            self.writeLine(ColourTuple(config.colour_echo), msg.nick + ": ")
            self.writeText(ColourTuple(config.colour_text), msg.text)
        
            
        
class InputPanel(wx.Panel):
    def __init__(self, parent, chatpanel):
        wx.Panel.__init__(self, parent)
        self.chatpanel = chatpanel
        self.text = wx.TextCtrl(self, style=wx.TE_MULTILINE |
                                wx.BORDER_NONE |
                                wx.TE_PROCESS_ENTER)
        sizer = wx.BoxSizer()
        sizer.Add(self.text, 1, wx.EXPAND)
        self.SetSizerAndFit(sizer)
        self.text.Bind(wx.EVT_TEXT_ENTER, self.onSend)        
        
    def onSend(self, evt):
        text = self.text.GetValue()
        self.text.SetValue("")
        
        if text[0] == "/":
            msg = text[1:]
        else:
            self.chatpanel.echoOwn(text)
            msg = "PRIVMSG %s %s" % (self.chatpanel.name, text)
        msg = msg.encode(config.default_encoding)
        self.chatpanel.connection.transmitter.send(msg)
        
class ChannelMemberListPanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.list = wx.ListCtrl(self)
        sizer = wx.BoxSizer()
        sizer.Add(self.list, 1, wx.EXPAND)
        self.SetSizerAndFit(sizer)
        
    def processMessage(self, msg):
        pass

class NavigationPanel(wx.Panel):
    '''The Panel on the left side with servers, channels and queries'''
    def __init__(self, parent, mainwindow):
        wx.Panel.__init__(self, parent)
        self.mainwindow = mainwindow
        self.tree = wx.TreeCtrl(self, style=wx.TR_FULL_ROW_HIGHLIGHT |
                                        wx.TR_HAS_BUTTONS |
                                        wx.TR_TWIST_BUTTONS)
        sizer = wx.BoxSizer()
        sizer.Add(self.tree, 1, wx.EXPAND)
        self.SetSizer(sizer)
        
        self.tree_items = {}
        self.root = self.tree.AddRoot(s.nav_root)
        
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.onLeftClick)
        
    def addConnection(self, server):
        item = self.tree.AppendItem(self.root, server)
        self.tree_items[server] = item #remember it
        self.tree.Expand(self.root)
        self.tree.SelectItem(item)
    
    def addChannel(self, server, channel):
        server_item = self.tree_items[server]
        item = self.tree.AppendItem(server_item, channel)
        self.tree_items[channel + "@" + server] = item #remember it
        self.tree.Expand(server_item)
        self.tree.SelectItem(item)
    
    def addQuery(self, server, nick):
        server_item = self.tree_items[server]
        item = self.tree.AppendItem(server_item, nick)
        self.tree_items[nick + "@" + server] = item #remember it
        self.tree.Expand(server_item)
        self.tree.SelectItem(item)
    
    def setQueryNick(selfself, server, oldnick, newnick):
        pass
    
    def onLeftClick(self, evt):
        item = self.tree.GetSelection()
        name = self.tree.GetItemText(item)
        root = self.tree.GetItemText(self.tree.GetItemParent(item))
        
        for panel in self.mainwindow.chatpanels:
            if (panel.name == name and panel.connection.server == root):
                self.mainwindow.p_chat.showPanel(panel)
        
        evt.Skip()

class ChatPanel(wx.Panel):
    def __init__(self, parent, connection, name):
        wx.Panel.__init__(self, parent, -1)
        self.name = name
        self.connection = connection
        
    def echoOwn(self, text):
        msg = irc.Message(self.connection, "")
        msg.command = "echo"
        msg.room = self.name
        msg.nick = self.connection.nick
        msg.text = text
        self.processMessage(msg)
        
    def processMessage(self, msg):
        raise NotImplementedError

class ChannelPanel(ChatPanel):
    def __init__(self, parent, connection, name):
        ChatPanel.__init__(self, parent, connection, name)

        vsplit = wx.SplitterWindow(self, 
                                      style = wx.SP_NOBORDER
                                      | wx.SP_LIVE_UPDATE)
        pleft = wx.Panel(vsplit)
        self.p_list = ChannelMemberListPanel(vsplit)
        vsplit.SplitVertically(pleft, self.p_list)
        vsplit.SetSashGravity(1)
        vsplit.SetMinimumPaneSize(120)
        
        hsplit = wx.SplitterWindow(pleft, 
                                      style = wx.SP_3D 
                                      | wx.SP_LIVE_UPDATE)
        self.p_out = OutputPanel(hsplit)
        self.p_in = InputPanel(hsplit, self)
        hsplit.SplitHorizontally(self.p_out, self.p_in)
        hsplit.SetSashGravity(1)
        hsplit.SetMinimumPaneSize(20)
        
        # sizers
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(main_sizer)
        main_sizer.Add(vsplit, 1, wx.EXPAND)
        main_sizer.Fit(self)
        
        left_sizer = wx.BoxSizer(wx.VERTICAL)
        pleft.SetSizer(left_sizer)
        left_sizer.Add(hsplit, 1, wx.EXPAND)
        left_sizer.Fit(pleft)
        
    def processMessage(self, msg):
        if (msg.command in ["chat", "join", "part", "echo"]):
            self.p_list.processMessage(msg)
            self.p_out.processMessage(msg)
            
        if msg.command == "quit":
            if self.p_list.hasMember(msg.nick):
                self.p_list.processMessage(msg)
                self.p_out.processMessage(msg)
            
        if msg.command == "room_members":
            self.p_list.processMessage(msg)

class QueryPanel(ChatPanel):
    def __init__(self, parent, connection, name):
        ChatPanel.__init__(self, parent, connection, name)
        hsplit = wx.SplitterWindow(self, 
                                      style = wx.SP_NOBORDER
                                      | wx.SP_LIVE_UPDATE)
        self.p_out = OutputPanel(hsplit)
        self.p_in = InputPanel(hsplit, self)
        hsplit.SplitHorizontally(self.p_out, self.p_in)
        hsplit.SetSashGravity(1)
        hsplit.SetMinimumPaneSize(20)
        
        # sizers
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.SetSizer(main_sizer)
        main_sizer.Add(hsplit, 1, wx.EXPAND)
        main_sizer.Fit(self)
        
    def processMessage(self, msg):
        if msg.command in ["im", "echo"]:
            self.p_out.processMessage(msg)
    
        
class ChatPanelContainer(wx.Panel):
    '''Container for multiple ChannelPanel and/or QueryPanel instances'''
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.sizer = wx.BoxSizer()
        self.SetSizer(self.sizer)
        self.panels = []
        
    def addPanel(self, panel, show=False):
        self.panels.append(panel)
        if show:
            self.showPanel(panel)
    
    def showPanel(self, panel):
        for other_panel in self.panels:
            other_panel.Hide()
        self.current_panel = panel
        self.sizer.Clear()
        self.sizer.Add(self.current_panel, 1, wx.EXPAND)
        self.current_panel.Show()
        self.Layout()
     
class MainWindow(wx.Frame):
    connections = [] #FIXME: (see below)
    chatpanels = [] #FIXME: this seems redundant, we already have a treectrl
    
    def __init__(self):
        wx.Frame.__init__(self, None, -1, s.window_title_main, size=(700, 400))
        self.Bind(wx.EVT_CLOSE, self.onCloseWindow)        
        self.setupUI()

        self.Show()
        
        for testconf in config.test_server:
            srv = testconf[0]
            c = self.addConnection(srv, 6667, "p7irc_prototype", "bernd", "http://p7irc.googlecode.com/")
            for chan in testconf[1:]:
                c.join(chan)
        
    def onCloseWindow(self, evt):
        for connection in self.connections:
            connection.close()
        
        sys.exit(0) #at least this works for sure

    def setupUI(self):
        self.panel = wx.Panel(self)
        splitter = wx.SplitterWindow(self.panel, style=wx.SP_NOBORDER)
        self.p_nav = NavigationPanel(splitter, self)
        self.p_chat = ChatPanelContainer(splitter)
        splitter.SplitVertically(self.p_nav, self.p_chat, 150)
        sizer = wx.BoxSizer()
        sizer.Add(splitter, 1, wx.EXPAND | wx.ALL, 3)
        self.panel.SetSizer(sizer)
        
    def NetworkCallback(self, msg):
        '''This is the one and only point, where the network
        layer can talk to the gui.'''
        
        panel = None
        if msg.room != "":
            for p in self.chatpanels:
                if (p.name == msg.room and p.connection == msg.connection):
                    panel = p
                    break
            
            if not panel:
                panel = self.addChatpanel(msg.connection, msg.room)
            
            panel.processMessage(msg)
            
        else:
            if msg.command == "im":
                for p in self.chatpanels:
                    if (p.name == msg.nick and p.connection == msg.connection):
                        panel = p
                        break
                
                if not panel:
                    panel = self.addQueryPanel(msg.connection, msg.nick)
                
                panel.processMessage(msg)
        
    def addConnection(self, server, port, nick, ident, name):
        conn = irc.Connection(self.NetworkCallback, server, port, nick, ident, name)
        self.connections.append(conn) #FIXME: redundant
        self.p_nav.addConnection(conn.server)
        return conn
            
    def addChatpanel(self, connection, name):
        panel = ChannelPanel(self.p_chat, connection, name)
        self.chatpanels.append(panel) #FIXME: redundant
        self.p_nav.addChannel(connection.server, name)
        self.p_chat.addPanel(panel, True)
        return panel

    def addQueryPanel(self, connection, name):
        panel = QueryPanel(self.p_chat, connection, name)
        self.chatpanels.append(panel) #FIXME: redundant
        self.p_nav.addQuery(connection.server, name)
        self.p_chat.addPanel(panel, True)
        return panel

class Application(wx.App):
    def OnInit(self):
        self.mw = MainWindow()
        self.SetTopWindow(self.mw)
        return True
    
        
def main():
    global app 
    app = Application(redirect=False)
    app.MainLoop()        
                    
                    
if __name__ == '__main__':
    main()